package org.design.singleton;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 一个非常简单的单例模式 
 * 1. 私有静态的一个自己 
 * 2. 构造函数私有，这样的话，就不能随意new实例了 
 * 3. 用一个静态方法获得一个实例
 * 4. 针对反射和序列化，我们需要做点额外的事情
 * 
 * @author chenxianpeng
 *
 */
public class SimpleSingleton2 implements Serializable {
	private static SimpleSingleton2 _instance = null;
	
	private String value;

	private SimpleSingleton2() {
		// TODO Auto-generated constructor stub
		// 防止反射获取多个对象的漏洞 
		// 这种方式只能防止已经执行过getInstance时的代码。
		//因为没有执行getInstance时，_instance永远是null的。
        if (null != _instance) {  
            throw new RuntimeException();  
        }  
        value = "init";
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public static SimpleSingleton2 getInstance() {
		if (_instance == null) {
			_instance = new SimpleSingleton2();
		}
		return _instance;
	}
	
	// 防止反序列化获取多个对象的漏洞。  
    // 无论是实现Serializable接口，或是Externalizable接口，当从I/O流中读取对象时，readResolve()方法都会被调用到。  
    // 实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。  
    private Object readResolve() 
    		throws ObjectStreamException {    
        return _instance;  
    }  

	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, IOException {
		// TODO Auto-generated method stub
		// 大部分时候，我们一般情况下，代码就写到这里了。
		SimpleSingleton2 a1 = SimpleSingleton2.getInstance();
		SimpleSingleton2 a2 = SimpleSingleton2.getInstance();
		System.out.println("a1=" + a1);
		System.out.println("a2=" + a2);
		System.out.println("equal?" + (a1 == a2));

		// 如果碰到反射呢？
		// 通过反射的方式直接调用私有构造器（通过在构造器里抛出异常可以解决此漏洞）
//		Class<SimpleSingleton2> clazz = (Class<SimpleSingleton2>) Class.forName("org.design.singleton.SimpleSingleton2");
//		Constructor<SimpleSingleton2> c = clazz.getDeclaredConstructor(null);
//		c.setAccessible(true); // 跳过权限检查
//		SimpleSingleton2 ss3 = c.newInstance();
//		SimpleSingleton2 ss4 = c.newInstance();
//		System.out.println("ss3=" + ss3); // ss3，ss4不是同一个对象
//		System.out.println("ss4=" + ss4);
		
		//如果碰到序列化 反序列化呢？
		//我们的类首先要支持序列化，不支持的话，就无所谓这个问题了。
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		System.out.println("value before writeObject=" + a1.getValue());
		oos.writeObject(a1);  
        oos.close();  
        a1.setValue("update after serial");
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        SimpleSingleton2 ss5 = (SimpleSingleton2) ois.readObject();
        ois.close();
        System.out.println("ss5=" + ss5);
        System.out.println("a1=" + a1.getValue() + " se5=" + ss5.getValue());
	}

}
